package cn.lovecyy.mockjson;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.lovecyy.mockjson.core.domain.TypeConfig;

import cn.lovecyy.mockjson.core.exception.MockException;
import cn.lovecyy.mockjson.core.funcs.FuncCall;
import cn.lovecyy.mockjson.core.funcs.FuncMock;
import cn.lovecyy.mockjson.core.funcs.FuncMockRegistry;
import cn.lovecyy.mockjson.core.template.TemplateLoader;
import cn.lovecyy.mockjson.core.type.TypeMocker;
import cn.lovecyy.mockjson.core.type.TypeMockerRegistry;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

/**
 * MockHelper
 *
 * @author Yakir
 */
@Slf4j
public class MockKit {

	public static String render(String filePath) {
		// 加载模板
		JSONObject template = TemplateLoader.loadJsonTemplateByFile(new File(filePath));
		// 上下文
		Map<String, Object> context = new HashMap<>();
		// 执行mock
		String result = mock(template, context);
		return result;
	}

	public static void main(String[] args) {
		String dir = "D:\\idea\\source\\person2\\gmsandbox\\docs\\json\\";
		String creditApplyName = "CreditApply.json";
		String creditQueryName = "CreditQuery.json";
		String useApplyName = "UseApply.json";
		String useQueryName = "UseQuery.json";

		String jsonStr = "{\"loanNo\":\"ctx|loanNo|\",\"partnerBizNo\":\"text|18|\",\"seqNo\":\"string|10-18|\",\"id\":\"text|abcd:toUpper|\",\"extid\":\"text|'abcd:aaa':toUpper|\",\"age\":\"int|18|\",\"repeatCnt\":\"repeat|1|8\",\"ageRange\":\"int|20-22|\",\"gender\":\"enum|男,女|\",\"email\":\"email||\",\"reqTime\":\"timestamp||10\",\"funcdate\":\"date|:fmtDate(yyyyMMdd,yyyy-MM-dd)|yyyyMMdd\",\"funcdateSec\":\"date|'2025-01-01':fmtDate(yyyy-MM-dd,yyyyMMdd)|yyyyMMdd\",\"funcdateTimeSec\":\"datetime|'2025-01-01 12:12:12':fmtDate(yyyy-MM-dd HH:mm:ss,yyyy年MM月dd日 HH时mm分ss秒)|\",\"date\":\"date||yyyyMMdd\",\"dateSec\":\"date|2025-12-18|yyyyMMdd\",\"dateTime\":\"datetime||yyyyMMddHHmmss\",\"dateTimeSec\":\"datetime|'2025-12-18 12:10:10'|yyyyMMddHHmmss\",\"amt\":\"decimal|10-20|#.##\",\"ocrInfo\":{\"name\":\"ctx|name|person\",\"idCard\":\"ctx|idCard|person\"},\"relations\":[{\"relatedName\":\"ctx|p1.name|person\",\"provinceCode\":\"enum|1,2,3\"}],\"backUserName\":\"ref|ocrInfo.name|\",\"relatedName\":\"ref|relations[0].relatedName|\",\"bankInfo\":{\"baneNo\":\"ctx|b1.bankCardNo|bank\"}}";
		// String jsonStr = " {\"contact_name\": \"ctx|r1.name|\"},";
		// 加载模板
		JSONObject template = TemplateLoader.loadJsonTemplate(jsonStr);
		// 上下文
		Map<String, Object> context = new HashMap<>();
		context.put("loanNo", IdUtil.getSnowflakeNextIdStr());
		// 执行转换
		Object mockData = mockFromTemplate(template, context, "");
		String mockDataa = mock(template, context);
		System.out.println(JSONUtil.toJsonPrettyStr(mockData));
		// System.out.println(context);
		String creditApplyJson = render(dir + creditApplyName);
		System.out.println(creditApplyJson);
		String creditQueryJson = render(dir + creditQueryName);
		System.out.println(creditQueryJson);
		String useApplyJson = render(dir + useApplyName);
		System.out.println(useApplyJson);
		String useQueryJson = render(dir + useQueryName);
		System.out.println(useQueryJson);
	}

	public static String mock(Object jsonObj, Map<String, Object> context) {
		return JSONUtil.toJsonStr(mockFromTemplate(jsonObj, context, ""));
	}

	public static String mock(Object jsonObj, Map<String, Object> context, String parentKey) {
		return JSONUtil.toJsonStr(mockFromTemplate(jsonObj, context, parentKey));
	}

	// 2. 递归生成 mock 数据，优先用 context
	public static Object mockFromTemplate(Object jsonObj, Map<String, Object> context, String parentKey) {
		if (jsonObj instanceof JSONObject) {
			JSONObject obj = (JSONObject) jsonObj;
			JSONObject result = new JSONObject();
			for (String key : obj.keySet()) {
				Object value = obj.get(key);
				String fullKey = StrUtil.isBlank(parentKey) ? key : parentKey + "." + key;
				if (value instanceof String) {
					Object mockVal = mockValueByType(fullKey, (String) value, context);
					result.put(key, mockVal);
					// 将 mock 结果放入 context，便于后续 key 引用
					context.put(fullKey, mockVal);
				}
				else {
					result.put(key, mockFromTemplate(value, context, fullKey));
				}
			}
			return result;
		}
		else if (jsonObj instanceof JSONArray) {
			JSONArray arr = (JSONArray) jsonObj;
			JSONArray result = new JSONArray();
			for (int i = 0; i < arr.size(); i++) {
				Object item = arr.get(i);
				String fullKey = parentKey + "[" + i + "]";
				result.add(mockFromTemplate(item, context, fullKey));
			}
			return result;
		}
		else {
			return jsonObj;
		}
	}

	// 3. 按类型描述生成 mock 值
	public static Object mockValueByType(String key, String typeStr, Map<String, Object> context) {
		try {
			TypeConfig config = TypeConfig.parse(key, typeStr);
			config.setContext(context);
			if (!typeStr.contains("|")) {
				return typeStr;
			}
			TypeMocker mocker = TypeMockerRegistry.get(config.type);
			if (mocker == null) {
				log.warn("Type not found: " + config.type + " for key: " + key);
				throw new MockException("Type not found: " + config.type);
			}
			Object value = mocker.mock(config);
			// 所有类型都支持函数链
			if (config.getFunctionChain() != null && !config.getFunctionChain().isEmpty()) {
				for (FuncCall func : config.getFunctionChain()) {
					FuncMock funcMock = FuncMockRegistry.get(func.getName());
					if (funcMock != null) {
						value = funcMock.apply(config, mergeArgs(value, func.getArgs()));
					}
				}
			}

			return value == null ? "" : value;
		}
		catch (Exception e) {
			log.error("Mock failed for key: " + key + ", type: " + typeStr, e);
			throw new MockException("Mock failed for key: " + key, e);
		}
	}

	// 合并主值和参数
	private static String[] mergeArgs(Object value, java.util.List<String> args) {
		String[] arr = new String[(args == null ? 0 : args.size()) + 1];
		arr[0] = value == null ? null : value.toString();
		if (args != null) {
			for (int i = 0; i < args.size(); i++) {
				arr[i + 1] = args.get(i);
			}
		}
		return arr;
	}

}
